﻿using System;

using Exif.Jpeg.Enums;

namespace Exif.Jpeg.Segments
{
	///<summary>
	///Фабрика блоков
	///</summary>
	public static class JpegSegmentFactory
	{
		#region Статик методы

		///<summary>
		///Создает новый блок
		///</summary>
		///<param name="provider"></param>
		///<param name="offset"></param>
		///<returns></returns>
		public static JpegSegmentBase CreateSegment(JpegImageProvider provider, int offset)
		{
			JpegSegmentBase segment;
			lock (provider.ImageReader.SyncRoot)
			{
				provider.ImageReader.BeginRead(offset);

				var code = provider.ImageReader.ReadCode();

				switch (code)
				{
					case (ushort)JpegMarker.SOI:
						segment = CreateSOISegment(provider, (JpegMarker)code, offset);
						break;

					case (ushort)JpegMarker.DHT:
						segment = CreateDHTSegment(provider, (JpegMarker)code, offset);
						break;

					case (ushort)JpegMarker.DQT:
						segment = CreateDQTSegment(provider, (JpegMarker)code, offset);
						break;

					case (ushort)JpegMarker.DRI:
						segment = CreateDRISegment(provider, (JpegMarker)code, offset);
						break;

					case (ushort)JpegMarker.SOS:
						segment = CreateSOSSegment(provider, (JpegMarker)code, offset);
						break;

					case (ushort)JpegMarker.COM:
						segment = CreateCOMSegment(provider, (JpegMarker)code, offset);
						break;

					case (ushort)JpegMarker.EOI:
						segment = CreateEOISegment(provider, (JpegMarker)code, offset);
						break;

					default:
						switch (code & 0xFFF0)
						{
							case (ushort)JpegMarker.SOFn:
								segment = CreateSOFnSegment(provider, (JpegMarker)code, offset, code);
								break;

							case (ushort)JpegMarker.RSTn:
								segment = CreateRSTnSegment(provider, (JpegMarker)code, offset, code);
								break;

							case (ushort)JpegMarker.APPn:
								segment = CreateAPPnSegment(provider, (JpegMarker)code, offset, code);
								break;

							default: //JpegMarker.ImageReader (не имеет маркера)
								segment = null;// CreateImageBataBlock(provider, JpegMarker.ImageReader, offset);
								break;
						}
						break;
				}

				provider.ImageReader.EndRead();
			}

			return segment;
		}

		#endregion

		#region Частные статик методы создания блоков

		private static JpegSegmentBase CreateSOISegment(JpegImageProvider provider, JpegMarker marker,
			int offset)
		{
			return new SegmentSOI(provider, marker, offset);
		}

		private static JpegSegmentBase CreateSOFnSegment(JpegImageProvider provider, JpegMarker marker,
			int offset, ushort code)
		{
			var sofNum = code & 0x000F;
			var length = ReadLength(provider);
			return new SegmentSOFn(provider, marker, offset, length, sofNum);
		}

		private static JpegSegmentBase CreateDHTSegment(JpegImageProvider provider, JpegMarker marker,
			int offset)
		{
			var length = ReadLength(provider);
			return new SegmentDHT(provider, marker, offset, length);
		}

		private static JpegSegmentBase CreateDQTSegment(JpegImageProvider provider, JpegMarker marker,
			int offset)
		{
			var length = ReadLength(provider);
			return new SegmentDQT(provider, marker, offset, length);
		}

		private static JpegSegmentBase CreateDRISegment(JpegImageProvider provider, JpegMarker marker,
			int offset)
		{
			var length = ReadLength(provider);
			return new SegmentDRI(provider, marker, offset, length);
		}

		private static JpegSegmentBase CreateSOSSegment(JpegImageProvider provider, JpegMarker marker,
			int offset)
		{
			var length = ReadLength(provider);
			return new SegmentSOS(provider, marker, offset, length);
		}

		private static JpegSegmentBase CreateRSTnSegment(JpegImageProvider provider, JpegMarker marker,
			int offset, ushort code)
		{
			var rstKind = (RSTn)(code & 0x000F);
			return new SegmentRSTn(provider, marker, offset, rstKind);
		}

		private static JpegSegmentBase CreateAPPnSegment(JpegImageProvider provider, JpegMarker marker,
			int offset, ushort code)
		{
			var appNum = code & 0x000F;
			var length = ReadLength(provider);
			return SegmentAPPn.CreateAPPnBlock(provider, marker, offset, length, appNum);
		}

		private static JpegSegmentBase CreateCOMSegment(JpegImageProvider provider, JpegMarker marker,
			int offset)
		{
			var length = ReadLength(provider);
			return new SegmentCOM(provider, marker, offset, length);
		}

		private static JpegSegmentBase CreateEOISegment(JpegImageProvider provider, JpegMarker marker,
			int offset)
		{
			return new SegmentEOI(provider, marker, offset);
		}

		//private static JpegSegmentBase CreateImageBataBlock(JpegImageProvider provider, JpegMarker marker,
		//    int offset)
		//{
		//    var length = ReadImageDataLength(provider);
		//    return new SegmentImageData(provider, marker, offset, length);
		//}

		#endregion

		#region Частные статик методы

		private static int ReadLength(JpegImageProvider provider)
		{
			var len = provider.ImageReader.ReadInt16(); //uint16?
			return len;
		}

		//private static int ReadImageDataLength(JpegImageProvider provider)
		//{
		//    return provider.ImageReader.Length - 2 /*EOI block*/-
		//        (provider.ImageReader.Position - 2 /*в этом блоке нет маркера*/);
		//}

		#endregion
	}
}